package com.api.client.impl;

import java.lang.reflect.Type;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.entity.ContentType;

import com.api.client.model.ApiErrorHandler;
import com.api.client.model.ApiResponse;
import com.api.client.model.ApiResultException;
import com.api.client.model.ApiResultHandler;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.LongSerializationPolicy;

public class ApiResultHandlerImpl implements ApiResultHandler {

	private final Log logger = LogFactory.getLog(getClass());

	private static final int HTTP_SUCCESS_CODE = 200;

	private static final int HTTP_REDIRECTION_CODE = 300;

	private final ApiErrorHandler<?> errorHandler;

	public ApiResultHandlerImpl(ApiErrorHandler<?> errorHandler) {

		if (errorHandler == null) {
			throw new IllegalArgumentException("ErrorHandler cannot be null");
		}
		this.errorHandler = errorHandler;
	}

	public Object getResponseResult(ApiResponse response, String produces, Type returnType) throws Exception {

		if (response == null || produces == null || returnType == null) {
			throw new IllegalArgumentException("Response, produces and returnType cannot be null");
		}

		if (isResponseSuccessful(response.getCode())) {

			Object result = null;

			if (produces.equals(ContentType.APPLICATION_JSON.getMimeType())) {

				result = getFromJSon(returnType, response.getBody());
			} else if (produces.equals(ContentType.APPLICATION_OCTET_STREAM.getMimeType())) {

				result = response.getContent();
			} else {
				result = response.getBody();
			}
			return result;
		} else {
			handleError(response.getCode(), response.getBody());

			throw new ApiResultException(String.format("Response not successful - Code: %s Body: '%s'", response.getCode(), response.getBody()));
		}
	}

	private boolean isResponseSuccessful(int responseCode) {

		return responseCode >= HTTP_SUCCESS_CODE && responseCode < HTTP_REDIRECTION_CODE;
	}

	private void handleError(int responseCode, String errorMessage) throws Exception {

		logger.warn(String.format("Response not successful - Code: %s Body: '%s'", responseCode, errorMessage));

		if (errorMessage != null && !errorMessage.equals("")) {

			Object errorObject = getFromJSon(errorHandler.getErrorType(), errorMessage);

			errorHandler.throwException(errorObject);
		}
	}

	private <T> T getFromJSon(Type returnType, String body) {

		GsonBuilder gs = new GsonBuilder();
		gs.serializeNulls();
		gs.disableHtmlEscaping();
		gs.setLongSerializationPolicy(LongSerializationPolicy.STRING);

		Gson gson = gs.create();
		try {
			return gson.fromJson(body, returnType);
		} catch (Exception e) {

			throw new IllegalStateException(String.format("Couln't parse json '%s' to class '%s'", body, returnType), e);
		}
	}
}
